<template>
  <TsDialog v-bind="dialogConfig" @on-close="close">
    <template v-slot>
      <div style="position:relative">
        <div style="position:absolute;right:0px;top:0px" class="layout">
          <span
            v-for="(layout, index) in layoutList"
            :key="index"
            class="item fz10"
            :class="searchParam.layout === layout.engine ? 'text-primary' : 'text-grey'"
            style="cursor:pointer"
            @click="changeLayout(layout.engine)"
          >
            {{ layout.name }}
          </span>
        </div>
        <div id="graph" :style="error ? 'display:none' : ''" class="home-page"></div>
        <div v-if="error" class="text-grey" style="padding-top:50px;text-align:center">
          {{ error }}
        </div>
        <Loading :loadingShow="isloading" type="fix"></Loading>
      </div>
    </template>
  </TsDialog>
</template>
<script>
import * as d3 from 'd3-selection';
import '@/resources/assets/font/tsfont/font/tsfont.js';
import '@/resources/assets/font/tsfonts/iconfont.js';
import { graphviz } from 'd3-graphviz';
import { addEvent } from '@/resources/assets/js/event.js';

export default {
  name: '',
  components: {},
  props: {
    graphId: { type: Number }
  },
  data() {
    return {
      dialogConfig: {
        title: this.$t('dialog.title.graphrel'),
        type: 'modal',
        maskClose: false,
        isShow: true,
        width: 'large'
      },
      isloading: false,
      layoutList: [
        { engine: 'circo', name: this.$t('term.cmdb.topo.circo') },
        { engine: 'neato', name: this.$t('term.cmdb.topo.neato') },
        { engine: 'osage', name: this.$t('term.cmdb.topo.osage') },
        { engine: 'fdp', name: this.$t('term.cmdb.topo.fdp') },
        { engine: 'twopi', name: this.$t('term.cmdb.topo.twopi') }
      ],
      searchParam: { layout: 'neato', graphId: this.graphId },
      graph: {},
      data: {},
      error: null
    };
  },
  beforeCreate() {},
  created() {},
  beforeMount() {},
  mounted() {
    this.initGraph();
  },
  beforeUpdate() {},
  updated() {},
  activated() {},
  deactivated() {},
  beforeDestroy() {},
  destroyed() {},
  methods: {
    close() {
      this.$emit('close');
    },
    changeLayout(layout) {
      this.searchParam.layout = layout;
    },
    loadImage(nodesString) {
      (nodesString.match(/image=[^,]*(files\/\d*|png)/g) || [])
        .filter((value, index, self) => {
          return self.indexOf(value) === index;
        })
        .map(keyvaluepaire => keyvaluepaire.substr(7))
        .forEach(image => {
          this.graph.graphviz.addImage(image, '48px', '48px');
        });
    },
    getGraphTop(object, offset) {
      offset = offset || { x: 0, y: 0 };
      offset.x += object.offsetLeft;
      offset.y += object.offsetTop;
      if (object.offsetParent) {
        offset = this.getGraphTop(object.offsetParent, offset);
      }
      return offset;
    },
    resizeSVG() {
      const graphEl = document.getElementById('graph');
      if (graphEl) {
        d3.select('#graph')
          .selectWithoutDataPropagation('svg')
          .transition()
          .duration(700)
          .attr('width', graphEl.offsetWidth - 10)
          .attr('height', window.innerHeight - 30 - this.getGraphTop(graphEl).y);
      }
    },
    initGraph() {
      window.setTimeout(() => {
        const graphEl = document.getElementById('graph');
        let graph = d3.select('#graph');
        graph
          .on('dblclick.zoom', null)
          .on('wheel.zoom', null)
          .on('mousewheel.zoom', null);
        this.graph.graphviz = graph
          .graphviz()
          .height(window.innerHeight - 30 - this.getGraphTop(graphEl).y)
          .width(graphEl.offsetWidth - 10)
          .zoom(true)
          .fit(true)
          .attributer(function(d) {
            if (d.attributes.class === 'edge') {
              let keys = d.key.split('->');
              let from = keys[0].trim();
              let to = keys[1].trim();
              d.attributes.from = from;
              d.attributes.to = to;
            }

            if (d.tag === 'text') {
              let key = d.children[0].text;
              d3.select(this).attr('text-key', key);
            }
          });
        this.renderGraph();
        d3.select(window).on('resize', this.resizeSVG);
      }, 0);
    },
    renderGraph() {
      this.isloading = true;
      this.error = '';
      const graphEl = document.getElementById('graph');
      this.$api.cmdb.graph.getGraphTopo(this.searchParam).then(res => {
        try {
          const nodesString = res.Return;
          this.loadImage(nodesString);
          this.graph.graphviz
            .transition()
            .height(window.innerHeight - 30 - this.getGraphTop(graphEl).y)
            .width(graphEl.offsetWidth - 10)
            .renderDot(nodesString)
            .on('end', () => {});
          //let svg = d3.select('#graph').select('svg');
          //svg.append('g').lower();
          addEvent('svg', 'mouseover', e => {
            this.unColorNode();
            e.preventDefault();
            e.stopPropagation();
          });
          addEvent('.graphnode', 'mouseover', async e => {
            e.preventDefault();
            e.stopPropagation();
            d3.selectAll('g').attr('cursor', 'pointer');
            this.g = e.currentTarget;
            this.nodeName = this.g.firstElementChild.textContent.trim();
            this.colorNode(this.nodeName);
          });
          addEvent('.graphnode', 'click', async e => {
            const g = e.currentTarget;
            if (g && g.id) {
              const ciid = g.id.replace('Graph_', '');
              this.toGraph(ciid);
            }
          });
        } catch (e) {
          const toponame = this.layoutList.find(layout => layout.engine == this.searchParam.layout);
          if (toponame) {
            this.error = '当前模型数据无法使用' + toponame.name + '生成拓扑图，请换一种布局。';
          }
        }
        this.isloading = false;
      });
    },
    unColorNode() {
      d3.selectAll('g path').attr('style', '');
      d3.selectAll('g polygon').attr('style', '');
      d3.selectAll('.edge text').attr('style', '');
    },
    colorNode(nodeName) {
      d3.selectAll('g[from="' + nodeName + '"] path').attr('style', 'stroke:orange !important');
      d3.selectAll('g[from="' + nodeName + '"] text').attr('style', 'fill:orange !important');
      d3.selectAll('g[from="' + nodeName + '"] polygon').attr('style', 'stroke:orange !important;fill:orange !important');
      d3.selectAll('g[to="' + nodeName + '"] path').attr('style', 'stroke:#6495ED !important');
      d3.selectAll('g[to="' + nodeName + '"] text').attr('style', 'fill:#6495ED !important');
      d3.selectAll('g[to="' + nodeName + '"] polygon').attr('style', 'stroke:#6495ED !important;fill:#6495ED !important');
    },
    toGraph: function(graphId) {
      if (graphId) {
        this.$router.push({
          path: '/graph-datav2/' + graphId
        });
      }
    }
  },
  filter: {},
  computed: {},
  watch: {
    searchParam: {
      handler: function(val) {
        this.renderGraph();
      },
      deep: true
    }
  }
};
</script>
<style lang="less" scope>
@import '../public/graphviz.less';
.layout {
  display: flex;
  .item {
    padding: 0 10px;
    position: relative;
    &:not(:last-child):after {
      content: '|';
      color: @dividing-color;
      top: 0;
      right: 0;
      position: absolute;
    }
  }
}
.corenode text{
  fill:red !important
}
</style>
